using NICWebApi.Extensions.Http.Models;
using NICWebApi.Extensions.Http.Options;

namespace NICWebApi.Extensions.Http.Middlewares;

/// <summary>
/// HTTP上下文信息中间件
/// </summary>
public class HttpContextInfoMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<HttpContextInfoMiddleware> _logger;
    private readonly HttpOptions _options;
    private readonly IHttpContextUtils _httpContextUtils;

    /// <summary>
    /// 初始化HTTP上下文信息中间件
    /// </summary>
    /// <param name="next">下一个中间件</param>
    /// <param name="logger">日志</param>
    /// <param name="options">HTTP选项</param>
    /// <param name="httpContextUtils">HTTP上下文工具</param>
    public HttpContextInfoMiddleware(
        RequestDelegate next,
        ILogger<HttpContextInfoMiddleware> logger,
        IOptions<HttpOptions> options,
        IHttpContextUtils httpContextUtils)
    {
        _next = next ?? throw new ArgumentNullException(nameof(next));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        _options = options?.Value ?? throw new ArgumentNullException(nameof(options));
        _httpContextUtils = httpContextUtils ?? throw new ArgumentNullException(nameof(httpContextUtils));
    }

    /// <summary>
    /// 执行中间件
    /// </summary>
    /// <param name="context">HTTP上下文</param>
    /// <returns>任务</returns>
    public async Task InvokeAsync(HttpContext context)
    {
        // 检查是否需要跳过处理
        if (ShouldSkipPath(context.Request.Path))
        {
            await _next(context);
            return;
        }

        // 创建上下文信息
        var contextInfo = _httpContextUtils.GetRequestInfo(_options.LogHeaders);

        // 启用计时器
        var watch = Stopwatch.StartNew();

        // 启用请求和响应日志
        if (_options.LogRequestResponse)
        {
            await LogRequestResponseAsync(context, contextInfo);
        }
        else
        {
            // 执行下一个中间件
            await _next(context);
        }

        // 停止计时器
        watch.Stop();
        contextInfo.ExecutionTime = watch.ElapsedMilliseconds;

        // 记录状态码
        contextInfo.StatusCode = context.Response.StatusCode;

        // 记录请求信息
        LogRequestInfo(contextInfo);
    }

    /// <summary>
    /// 检查是否应该跳过处理的路径
    /// </summary>
    private bool ShouldSkipPath(PathString path)
    {
        if (string.IsNullOrEmpty(path))
        {
            return false;
        }

        foreach (var excludedPath in _options.ExcludedPaths)
        {
            if (path.StartsWithSegments(excludedPath, StringComparison.OrdinalIgnoreCase))
            {
                return true;
            }
        }

        return false;
    }

    /// <summary>
    /// 记录请求信息
    /// </summary>
    private void LogRequestInfo(HttpContextInfo contextInfo)
    {
        var logLevel = contextInfo.StatusCode switch
        {
            var code when code >= 500 => LogLevel.Error,
            var code when code >= 400 => LogLevel.Warning,
            _ => LogLevel.Information
        };

        // 获取日志文件夹路径
        var logDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "HttpRequestsLogs", DateTime.Now.ToString("yyyy-MM-dd"));
        
        // 确保日志文件夹存在
        Directory.CreateDirectory(logDirectory);

        // 生成日志文件路径
        var logFilePath = Path.Combine(logDirectory, "logs.log");

        // 写入文件日志
        var logMessage = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] [{logLevel}] HTTP请求 [{contextInfo.Method}] {contextInfo.Path} 耗时 {contextInfo.ExecutionTime}ms 返回状态码 {contextInfo.StatusCode}，客户端IP: {contextInfo.ClientIpAddress}";
        File.AppendAllText(logFilePath, logMessage + Environment.NewLine);

        // 同时输出到标准日志
        _logger.Log(logLevel,
            "HTTP请求 [{Method}] {Path} 耗时 {ExecutionTime}ms 返回状态码 {StatusCode}，客户端IP: {ClientIp}",
            contextInfo.Method,
            contextInfo.Path,
            contextInfo.ExecutionTime,
            contextInfo.StatusCode,
            contextInfo.ClientIpAddress);
    }

    /// <summary>
    /// 记录请求和响应日志
    /// </summary>
    private async Task LogRequestResponseAsync(HttpContext context, HttpContextInfo contextInfo)
    {
        // 保存原始请求和响应流
        var originalRequestBody = context.Request.Body;
        var originalResponseBody = context.Response.Body;

        try
        {
            // 创建新的可重复读取的请求流
            using var requestMemoryStream = new MemoryStream();
            // 创建新的响应流
            using var responseMemoryStream = new MemoryStream();

            // 替换请求流
            context.Request.EnableBuffering();

            // 替换响应流
            context.Response.Body = responseMemoryStream;

            // 复制请求流
            if (context.Request.ContentLength.HasValue && context.Request.ContentLength.Value > 0)
            {
                await originalRequestBody.CopyToAsync(requestMemoryStream);
                requestMemoryStream.Position = 0;
                context.Request.Body = requestMemoryStream;

                // 记录请求体大小
                contextInfo.RequestBodySize = requestMemoryStream.Length;

                // 重置流位置
                requestMemoryStream.Position = 0;
            }

            // 执行下一个中间件
            await _next(context);

            // 记录响应体大小
            contextInfo.ResponseBodySize = responseMemoryStream.Length;

            // 复制响应流到原始响应流
            responseMemoryStream.Position = 0;
            await responseMemoryStream.CopyToAsync(originalResponseBody);
        }
        finally
        {
            // 恢复原始流
            context.Request.Body = originalRequestBody;
            context.Response.Body = originalResponseBody;
        }
    }
}